/**********************************************************************************
// WindowsGame Source
//
// Creation:	19 May 2007
// Updated:		04 Jun 2011
// Compiler:	Visual Studio 2010
//
// Notes:	    The class abstracts all the details of setting up a window and 
//				a game loop. The user of the class has to subclass it and 
//				overwrite the methods GameInit, GameLogic, GameDraw and
//				GameShutdown. Creating an object of the subclass and calling 
//              the methods Initialize() and Start() will run the game.
//
//              WindowsGame methods should be invoked only from these overriden 
//				methods, not from the constructor of the game class. That's because 
//				some of the attributes of WindowGame are only set after the 
//				Start() method is called.
//
**********************************************************************************/

#include "WindowsGame.h"

/**********************************************************************************/

HINSTANCE WindowsGame::hInstance = NULL;					// application instance 
HWND WindowsGame::windowHandle	 = NULL;					// window handle
int WindowsGame::windowWidth  = 0;							// window width
int WindowsGame::windowHeight = 0;							// window height

/**********************************************************************************/

WindowsGame::WindowsGame()
{
	windowIcon		= LoadIcon(NULL, IDI_APPLICATION);		// default app icon
	windowCursor	= LoadCursor(NULL, IDC_ARROW);			// default arrow cursor
	windowBgColor	= (HBRUSH)GetStockObject(BLACK_BRUSH);	// black background
	windowTitle		= string("Windows Game");				// default game title
	windowStyle		= WS_POPUP | WS_VISIBLE;				// fullscreen style
	windowMode		= FULLSCREEN;							// fullscreen mode
	windowWidth		= GetSystemMetrics(SM_CXSCREEN);		// default to screen width
	windowHeight	= GetSystemMetrics(SM_CYSCREEN);		// default to screen height
	windowPosX		= 0;									// initial window x position
	windowPosY		= 0;									// initial window y position
	midWindowX      = windowWidth/2;						// default to center of screen width
	midWindowY		= windowHeight/2;						// default to center of screen height
	gameWorldWidth  = windowWidth;							// default to the size of the window
	gameWorldHeight = windowHeight;							// default to the size of the window
	midGameWorldX   = midWindowX;							// default to center of the window
	midGameWorldY   = midWindowY;							// default to center of the window
}

/**********************************************************************************/

void WindowsGame::SetWindowMode(int mode)
{
	if (mode)
	{
		// windowed mode
		windowMode = WINDOWED;
		windowStyle = WS_OVERLAPPED | WS_SYSMENU | WS_VISIBLE; 
	} 
	else 
	{
		// fullscreen mode
		windowMode = FULLSCREEN;
		windowStyle = WS_EX_TOPMOST | WS_POPUP | WS_VISIBLE; 
	}
}

/**********************************************************************************/

void WindowsGame::SetWindowSize(int width, int height)
{ 
	// window size
	windowWidth = width; 
	windowHeight = height;

	// position for the middle of the window
	midWindowX = windowWidth/2;
	midWindowY = windowHeight/2;

	// set the window position to the center of the screen
	windowPosX = (GetSystemMetrics(SM_CXSCREEN)/2) - (windowWidth/2);
	windowPosY	= (GetSystemMetrics(SM_CYSCREEN)/2) - (windowHeight/2);

	// game world defaults to new window size
	gameWorldWidth  = windowWidth;							
	gameWorldHeight = windowHeight;
	midGameWorldX   = midWindowX;
	midGameWorldY   = midWindowY;
}

/**********************************************************************************/
// set the game world size

void WindowsGame::SetGameWorldSize(int width, int height)
{
	gameWorldWidth  = width;							
	gameWorldHeight = height;
	midGameWorldX   = gameWorldWidth/2;
	midGameWorldY   = gameWorldHeight/2;
}

/**********************************************************************************/

int WindowsGame::Text(char *text, int x, int y, COLORREF color)
{
	// this function draws the sent text on screen using the given color
	// it uses GDI, which is slow 

	// get the graphics device context
	HDC xdc = GetDC(windowHandle); 

	// set the colors for the text
	SetTextColor(xdc, color);

	// set text background mode to transparent so black isn't copied
	SetBkMode(xdc, TRANSPARENT);

	// draw the text
	TextOut(xdc, x, y, text, int(strlen(text)));

	// release the dc
	ReleaseDC(windowHandle, xdc);

	// return success
	return 1;
}

/**********************************************************************************/

void WindowsGame::GameInit() 
{
	// user must override this method
}


/**********************************************************************************/


void WindowsGame::GameLogic() 
{
	// user must override this method

	// for now quit when the user hits the ESC key
	EndsOnEscape();

}

/**********************************************************************************/

void WindowsGame::GameDraw() 
{
	// user must override this method
}

/**********************************************************************************/

void WindowsGame::GameShutdown() 
{
	// user must override this method
}


/**********************************************************************************/

bool WindowsGame::Initialize(HINSTANCE appInstance)
{
	hInstance = appInstance;

	// this will hold the window object
	WNDCLASSEX winObj; 
	
	// first fill in the window class structure
	winObj.cbSize           = sizeof(WNDCLASSEX);
	winObj.style			= CS_DBLCLKS | CS_OWNDC | CS_HREDRAW | CS_VREDRAW;
	winObj.lpfnWndProc	    = WindowProcedure;
	winObj.cbClsExtra		= 0;
	winObj.cbWndExtra		= 0;
	winObj.hInstance		= hInstance;
	winObj.hIcon			= windowIcon;
	winObj.hCursor		    = windowCursor; 
	winObj.hbrBackground	= windowBgColor;
	winObj.lpszMenuName	    = NULL;
	winObj.lpszClassName	= "WinObj";
	winObj.hIconSm          = windowIcon;

	if (!RegisterClassEx(&winObj))
		return false;

	// create window based on the defined window class structure
	windowHandle = CreateWindowEx(
		NULL,							// extended style
		"WinObj",						// window object
		windowTitle.c_str(),			// title
		windowStyle,
		windowPosX, windowPosY,			// initial x,y
		windowWidth, windowHeight,		// initial width, height
		NULL,							// handle to parent 
		NULL,							// handle to menu
		hInstance,						// instance of this application
		NULL);							// extra creation parms

	// when using windowed mode we need to adjusting the real client area to 
	// take the borders and controls size into account. The default behaviour
	// of windows is to shrink the area to fit the controls.
	if (windowMode == WINDOWED)
	{
		// the client window size we desire
		RECT winRect = {0, 0, windowWidth, windowHeight};

		// adjust the window rectangle
		AdjustWindowRectEx(&winRect,
			GetWindowStyle(windowHandle),
			GetMenu(windowHandle) != NULL,
			GetWindowExStyle(windowHandle));

		// update window position
		windowPosX = (GetSystemMetrics(SM_CXSCREEN)/2) - ((winRect.right - winRect.left)/2);
		windowPosY	= (GetSystemMetrics(SM_CYSCREEN)/2) - ((winRect.bottom - winRect.top)/2);

		// resize the window with a call to MoveWindow
		MoveWindow(
			windowHandle,                  // window handle
			windowPosX,                    // x position
			windowPosY,                    // y position
			winRect.right - winRect.left,  // width
			winRect.bottom - winRect.top,  // height
			TRUE);						   // repaint
	}

	return (windowHandle ? true : false);
}

/**********************************************************************************/

int WindowsGame::Start() 
{
	MSG msg;	// generic windows message

	// initialize game here
	GameInit();

	/**************************************/

	// enter main event loop
	while(true)
	{
		// test if there is a message in queue, if so get it
		if (PeekMessage(&msg,NULL,0,0,PM_REMOVE))
	    { 
			// test if this is a quit
			if (msg.message == WM_QUIT)
				break;
	
			// translate any accelerator keys
			TranslateMessage(&msg);

			// send the message to the window proc
			DispatchMessage(&msg);
		}
    
		/**************************************/
		/*             Game Main              */
		/**************************************/

		// game logic goes here
		GameLogic();

		// game draw calls goes here		
		GameDraw();

		/**************************************/
       
	} // end while

	// closedown game here
	GameShutdown();

	// return to Windows
	return int(msg.wParam);
}

/**********************************************************************************/

LRESULT CALLBACK WindowProcedure(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
	// this is the main message handler of the system
	PAINTSTRUCT		ps;		// used in WM_PAINT
	HDC				hdc;	// handle to a device context

	// what is the message 
	switch(msg)
	{
	case WM_CREATE:
		
		// do initialization stuff here
		break;
   
	case WM_PAINT: 

		// simply validate the window 
   	    hdc = BeginPaint(hWnd,&ps);	 
        
        // end painting
        EndPaint(hWnd,&ps);
   		break;

	case WM_SYSKEYDOWN:

		// intercept ALT-F4 key 
		// send a normal WM_DESTROY message
		if (wParam == VK_F4)
			PostMessage(hWnd, WM_DESTROY, 0, 0);
		break;

	case WM_DESTROY: 
		
		// this sends a WM_QUIT message to end application loop
		PostQuitMessage(0);
		break;

	default:
		return DefWindowProc(hWnd, msg, wParam, lParam);
    }
	return 0;
}

/**********************************************************************************/
